package com.smh.demo1.a23;

import com.smh.demo1.a23.WebConfig.Controller1;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.ModelFactory;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.*;

import java.lang.reflect.Method;
import java.util.List;

/**
 * @author shiminghui
 * @date 2025/5/3 8:34
 * @description: ServletInvocableHandlerMethod只是解析了 方法参数上的@ModelAttribute注解, 而RequestMappingHandlerAdapter 还会解析在方法上的@ModelAttribute注解
 */
public class A23 {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();


        // 模拟请求
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setMethod("GET");
        request.setParameter("c", "smh");

        // 初始化RequestMappingHandlerAdapter
        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        adapter.setApplicationContext(context);
        adapter.afterPropertiesSet();


        // 设置参数绑定工厂
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
        initializer.setConversionService(conversionService);

        ServletRequestDataBinderFactory binderFactory = new ServletRequestDataBinderFactory(null, initializer);


        // 创建ModelFactory
        Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
        getModelFactory.setAccessible(true);
        ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, new HandlerMethod(new Controller1(), Controller1.class.getMethod("test", String.class)), binderFactory);


        // 创建ServletInvocableHandlerMethod
        ServletInvocableHandlerMethod method = new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod("test", String.class));


        method.setDataBinderFactory(binderFactory);

        method.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());

        // 设置参数解析器
        HandlerMethodArgumentResolverComposite resolverComposite = new HandlerMethodArgumentResolverComposite();
        // todo 添加自定义的参数解析器
        resolverComposite.addResolvers(
                //    beanFactory环境变量和配置文件里的数据都要从这里面读取    false 表示必须有 @RequestParam
                new RequestParamMethodArgumentResolver(beanFactory, false),
                // 路径参数解析器
                new PathVariableMethodArgumentResolver(),
                // 请求头解析器
                new RequestHeaderMethodArgumentResolver(beanFactory),
                // Cookie解析器
                new ServletCookieValueMethodArgumentResolver(beanFactory),
                // 表达式解析器 即@Value注解
                new ExpressionValueMethodArgumentResolver(beanFactory),
                // request, response, session ...解析器
                new ServletRequestMethodArgumentResolver(),
                new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
                // @RequestBody 解析器
                new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),
                // 做一个保底操作, 有注解的优先处理没有的才会匹配这里,匹配成功再执行
                new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
                new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
        );
        method.setHandlerMethodArgumentResolvers(resolverComposite);


        ModelAndViewContainer container = new ModelAndViewContainer();
        // 初始化Model数据,即加载标注在类方法上面的@ModelAttribute注解
        modelFactory.initModel(new ServletWebRequest(request), container, method);

        // 执行方法
        method.invokeAndHandle(new ServletWebRequest(request), container);

        container.getModel().forEach((k, v) -> {
            System.out.println(k + "==" + v);
        });

        context.close();

    }
}
